package com.loong.data.handler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.loong.common.param.BaseIdParam;
import com.loong.common.rabbitmq.dto.GoodsBaseDTO;
import com.loong.common.utils.TransformUtils;
import com.loong.data.common.backend.param.GoodsQueryParam;
import com.loong.data.common.backend.param.PageParam;
import com.loong.data.common.backend.result.GoodsResult;
import com.loong.data.common.backend.result.GoodsSkuResult;
import com.loong.data.common.forehead.param.GoodsAndPageForeheadParam;
import com.loong.data.common.forehead.param.GoodsQueryForeheadParam;
import com.loong.data.common.forehead.param.PageForeheadParam;
import com.loong.data.common.forehead.result.GoodsForeheadResult;
import com.loong.data.common.forehead.result.GoodsSkuForeheadResult;
import com.loong.data.dto.GoodsDTOResult;
import com.loong.data.dto.GoodsSkuDTOResult;
import com.loong.data.model.GoodsModel;
import com.loong.data.repository.GoodsRepository;
import com.loong.data.utils.ESPageUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

/**
 * @Description: 处理商品业务的handler
 * @Author: 刘群
 * @Date: 2020/11/12 17:39
 */
@Slf4j
@Component
public class GoodsHandler {

    @Resource
    private GoodsRepository goodsRepository;

    @Resource
    private ElasticsearchRestTemplate template;

    @Resource
    private RestHighLevelClient client;

    /**
     * 新增商品信息到es：MQ消费者中调用该方法
     * @param goodsBaseDTO 商品参数
     */
    public void saveGoodsModel(GoodsBaseDTO goodsBaseDTO) {
        String jsonString = JSONArray.toJSONString(goodsBaseDTO);
        GoodsModel goodsModel = JSON.parseObject(jsonString).toJavaObject(GoodsModel.class);
        goodsRepository.save(goodsModel);
    }

    /**
     * 更新商品信息到es：MQ消费者中调用该方法
     * @param goodsBaseDTO 商品参数
     */
    public void updateGoodsModel(GoodsBaseDTO goodsBaseDTO) {
        //创建更新请求
        UpdateRequest request = new UpdateRequest("goods", goodsBaseDTO.getId().toString());
        //将要更新的数据以json格式封装到请求中
        request.doc(JSON.toJSONString(goodsBaseDTO), XContentType.JSON);
        //执行更新请求的操作
        try {
            client.update(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据id删除es中的商品信息：MQ消费者中调用该方法
     * @param id 商品id
     */
    public void deleteGoodsModelById(Long id) {
        goodsRepository.deleteById(id);
    }

    /**
     * 后台根据商品id查询商品
     * @param id 商品id
     * @return 商品
     */
    public GoodsResult findGoodsById(Long id){
        //根据商品id查询商品
        Optional<GoodsModel> optional = goodsRepository.findById(id);
        if (optional.isPresent()){
            String jsonString = JSONArray.toJSONString(optional.get());
            return JSON.parseObject(jsonString).toJavaObject(GoodsResult.class);
        }else {
            return null;
        }
    }

    /**
     * 前台根据商品id查询商品
     * @param param 商品id
     * @return 商品
     */
    public GoodsForeheadResult foreheadFindGoodsById(BaseIdParam param){
        //根据商品id查询商品
        Optional<GoodsModel> optional = goodsRepository.findById(param.getId());
        if (optional.isPresent()){
            String jsonString = JSONArray.toJSONString(optional.get());
            return JSON.parseObject(jsonString).toJavaObject(GoodsForeheadResult.class);
        }else {
            return null;
        }
    }


    /**
     * 后台分页查询商品
     * @param goodsParam 商品查询参数
     * @param pageParam 分页参数
     * @return 商品集合
     */
    public List<GoodsResult> pageQueryGoods(GoodsQueryParam goodsParam, PageParam pageParam){
        //将后台商品查询参数、分页参数转换成前台的商品查询参数、分页参数，然后封装到GoodsAndPageForeheadParam中
        GoodsQueryForeheadParam goodsForeheadParam = null;
        PageForeheadParam pageForeheadParam = null;
        if (ObjectUtils.isNotEmpty(goodsParam)){
            goodsForeheadParam = TransformUtils.transform(goodsParam,GoodsQueryForeheadParam.class);
        }
        if (ObjectUtils.isNotEmpty(pageParam)){
            pageForeheadParam = TransformUtils.transform(pageParam,PageForeheadParam.class);
        }
        GoodsAndPageForeheadParam param = GoodsAndPageForeheadParam.builder()
                .goodsQueryParam(goodsForeheadParam)
                .pageParam(pageForeheadParam)
                .build();
        //查询商品
        List<GoodsBaseDTO> goodsBaseDTOList = this.commonPageQueryGoods(param);
        if (CollectionUtils.isEmpty(goodsBaseDTOList)) return null;
        String jsonString = JSONArray.toJSONString(goodsBaseDTOList);
        return JSON.parseArray(jsonString).toJavaList(GoodsResult.class);
    }

    /**
     * 前台分页查询商品
     * @param goodsAndPageParam 查询和分页封装的参数
     * @return 商品信息集合
     */
    public List<GoodsForeheadResult> foreheadPageQueryGoods(GoodsAndPageForeheadParam goodsAndPageParam) {
        //获得GoodsBaseDTO集合
        List<GoodsBaseDTO> goodsBaseDTOList = this.commonPageQueryGoods(goodsAndPageParam);
        if (CollectionUtils.isEmpty(goodsBaseDTOList)) return null;
        String jsonString = JSONArray.toJSONString(goodsBaseDTOList);
        List<GoodsForeheadResult> goodsBaseList = JSON.parseArray(jsonString).toJavaList(GoodsForeheadResult.class);
      return  this.resultConsumer(goodsBaseList);
    }

    /**
     * 根据GoodsBase中的GoodsSku集合，按照sku的现价从小到大排序
     *  并过滤GoodsBase下没有GoodsSku的商品
     * @param goodsBaseList 商品集合
     * @return 商品集合
     */
    private List<GoodsForeheadResult> resultConsumer(List<GoodsForeheadResult> goodsBaseList) {
        if (CollectionUtils.isEmpty(goodsBaseList)) return null;
        //根据推荐字段进行排序：若商品的recommend值为true，则排在前面
        goodsBaseList.sort(Comparator.comparing(GoodsForeheadResult::getRecommend).reversed());
        return goodsBaseList.stream().filter(item ->
                !CollectionUtils.isEmpty(item.getGoodsSkuList())
        ).map(item -> {
            item.getGoodsSkuList().sort(Comparator.comparing(GoodsSkuForeheadResult::getCurrentPrice));
            return item;
        }).collect(toList());

    }

    /**
     * 公共方法：分页查询商品
     * @param goodsAndPageParam 查询和分页封装的参数
     * @return 商品信息集合
     */
    protected List<GoodsBaseDTO> commonPageQueryGoods(GoodsAndPageForeheadParam goodsAndPageParam) {
        PageForeheadParam pageParam = null;
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (ObjectUtils.isNotEmpty(goodsAndPageParam)){
            GoodsQueryForeheadParam param = goodsAndPageParam.getGoodsQueryParam();
            //构建查询条件
            if (ObjectUtils.isNotEmpty(param)) {
                if (ObjectUtils.isNotEmpty(param.getGoodsName())) {
                    //商品名称进行模糊查询
                    boolQuery.must(QueryBuilders.matchPhraseQuery("name", param.getGoodsName()));
                }
                if (ObjectUtils.isNotEmpty(param.getGoodsId())){
                    boolQuery.must(QueryBuilders.matchPhraseQuery("id",param.getGoodsId()));
                }
                if (ObjectUtils.isNotEmpty(param.getBrandId())){
                    boolQuery.must(QueryBuilders.matchPhraseQuery("brandId",param.getBrandId()));
                }
                if (ObjectUtils.isNotEmpty(param.getCategoryId())){
                    boolQuery.must(QueryBuilders.matchPhraseQuery("categoryId",param.getCategoryId()));
                }
                if (ObjectUtils.isNotEmpty(param.getShopId())){
                    boolQuery.must(QueryBuilders.matchPhraseQuery("shopId",param.getShopId()));
                }
                if (ObjectUtils.isNotEmpty(param.getRecommend())){
                    boolQuery.must(QueryBuilders.matchPhraseQuery("recommend",param.getRecommend()));
                }
            }else{
                boolQuery.must(QueryBuilders.matchAllQuery());
            }
            //分页参数
            if (ObjectUtils.isNotEmpty(goodsAndPageParam.getPageParam())){
                pageParam = goodsAndPageParam.getPageParam();
            }
        }else {
            boolQuery.must(QueryBuilders.matchAllQuery());
        }
        Pageable pageable = ESPageUtil.getPageableByParam(pageParam);
        return executeQuery(boolQuery,pageable);
    }

    /**
     * 执行查询请求，并获得结果
     * @param queryBuilder 查询条件
     * @param pageable 分页
     * @return 商品集合
     */
    private List<GoodsBaseDTO> executeQuery(QueryBuilder queryBuilder, Pageable pageable){
        //存放商品信息的集合
        List<GoodsModel> modelList = new ArrayList<>();
        //构建查询条件
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .withPageable(pageable)
                .build();
        //获得查询的结果
        SearchHits<GoodsModel> hits = template.search(searchQuery, GoodsModel.class);
        List<SearchHit<GoodsModel>> hitList = hits.getSearchHits();
        for (SearchHit<GoodsModel> hit : hitList) {
            GoodsModel model = hit.getContent();
            modelList.add(model);
        }
        return TransformUtils.transformList(modelList, GoodsBaseDTO.class);
    }

    /**
     * RPC接口的方法：根据商品id集合查询商品
     * @param idList 商品id集合
     * @return 商品集合
     */
    public List<GoodsDTOResult> findGoodsByIds(List<Long> idList) {
        List<GoodsModel> resultList = new ArrayList<>();
        Iterable<GoodsModel> all = goodsRepository.findAllById(idList);
        for (GoodsModel goodsModel : all) {
            resultList.add(goodsModel);
        }
        String jsonString = JSONArray.toJSONString(resultList);
        return JSON.parseArray(jsonString).toJavaList(GoodsDTOResult.class);
    }

    /**
     * RPC接口的方法：根据商品sku的id查询es中的商品
     * @param skuId 商品sku的id
     * @return 商品
     */
    public GoodsDTOResult findGoodsBySkuId(Long skuId){
        //构建嵌套查询条件
        TermQueryBuilder termQuery = QueryBuilders.termQuery("goodsSkuList.id", skuId);
        NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("goodsSkuList", termQuery, ScoreMode.Total);
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(nestedQuery)
                .build();
        SearchHits<GoodsModel> searchHits = template.search(searchQuery, GoodsModel.class);
        List<SearchHit<GoodsModel>> hits = searchHits.getSearchHits();
        if ((!CollectionUtils.isEmpty(hits)) && hits.size() == 1){
            GoodsModel goodsModel = hits.get(0).getContent();
            return TransformUtils.transform(goodsModel, GoodsDTOResult.class);
        }else {
            return null;
        }
    }
}
